package at.fhhagenberg.hydrac.backend.codegenerators;

import java.util.logging.Logger;

import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTActParamList;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTAddrOfStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTAlgDecl;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTAlgDef;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTAlgHead;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTAlgParamList;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTAndExpr;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTArraySpec;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTArrayType;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTAssignStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTAttrSpec;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTBlock;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTBreakStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTCallStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTChar;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTClassType;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTCompoundSpec;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTCompoundType;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTConstDecl;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTDeleteStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTElseBlock;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTExpr;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTExprList;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTFact;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTFalseFact;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTForStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTHaltStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTIdent;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTIdentList;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTIfStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTInheritedFact;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTInputStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTInteger;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTMethodDecl;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTMethodDef;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTNestedExpr;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTNewStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTNodeExt;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTNotFact;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTNullFact;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTOrExpr;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTOutputStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTParamKind;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTPointer;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTProgram;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTQualifier;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTReal;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTRelExpr;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTRepeatStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTReturnStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTReturnType;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTSelector;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTSimpleExpr;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTSingleOutput;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTStart;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTString;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTTerm;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTTextStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTThisFact;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTTrueFact;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTType;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTTypeDecl;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTTypeSpec;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTVarDecl;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTVarSpec;
import at.fhhagenberg.hydrac.backend.javacc.nodes.ASTWhileStat;
import at.fhhagenberg.hydrac.backend.javacc.nodes.SimpleNode;

//specific pas-codegenerator, implement abstract methods
public class PascalCodeGenerator extends AbstractCodeGenerator {

  public PascalCodeGenerator(String fileName, ASTStart astTree, int nrOfSpaces, Logger logger) {
    super(fileName, astTree, nrOfSpaces, "(*", "*)", logger);
  }

  @Override
  protected void handleASTActParamList(ASTActParamList node) {
    // TODO: check
    handleNode(node.jjtGetChild(0));
    handleNode(node.jjtGetChild(1));
  }

  @Override
  protected void handleASTAddrOfStat(ASTAddrOfStat node) {
    addString("addr(", node.getLineNumber());
    handleNode(node.jjtGetChild(0)); // Qualifier
    addString(")");
  }

  @Override
  protected void handleASTAlgDecl(ASTAlgDecl node) {
    handleNode(node.jjtGetChild(0)); // AlgHead
  }

  @Override
  protected void handleASTAlgDef(ASTAlgDef node) {
    enterScope();
    for (int i = 0; i < node.jjtGetNumChildren() - 1; i++) {
      // AlgHead (VarDecl)* Block
      // skip Ident
      handleNode(node.jjtGetChild(i));
    }
    addString(";");
    leaveScope();

    currentScopeName = "";
  }

  @Override
  protected void handleASTAlgHead(ASTAlgHead node) {
    boolean isFunc = false;
    boolean isMethod = false;
    boolean inMethodDef = false;
    boolean inMethodDecl = false;
    boolean isCtor = false;
    int lengthFuncParList = 0;

    // check if algorithm is a function
    isFunc = node.jjtGetChild(node.jjtGetNumChildren() - 1) instanceof ASTReturnType;

    // check if algorithm is a method
    inMethodDecl = node.jjtGetParent().jjtGetParent() instanceof ASTMethodDecl;
    inMethodDef = node.jjtGetParent().jjtGetParent() instanceof ASTMethodDef;
    isMethod = inMethodDef || inMethodDecl;

    if (inMethodDecl) {
      currentScopeName = ((ASTIdent) node.jjtGetParent().jjtGetParent().jjtGetParent().jjtGetParent().jjtGetChild(0)).getValue();
    } else if (inMethodDef) {
      currentScopeName = ((ASTIdent) node.jjtGetParent().jjtGetParent().jjtGetChild(0)).getValue();
    }

    // check if algorithm is a ctor
    isCtor = isMethod && currentScopeName.equals(((ASTIdent) node.jjtGetChild(0)).getValue());

    if (isFunc) {
      addString("FUNCTION ", node.getLineNumber());
      lengthFuncParList = node.jjtGetNumChildren() - 2;
    } else {
      if (isCtor) {
        addString("CONSTRUCTOR ", node.getLineNumber());
      } else {
        addString("PROCEDURE ", node.getLineNumber());
      }
      lengthFuncParList = node.jjtGetNumChildren() - 1;
    }

    if (inMethodDef) {
      addString(currentScopeName + ".");
    }

    if (isCtor) {
      addString("Init");
    } else {
      handleNode(node.jjtGetChild(0)); // Ident
    }

    // remember identifier for comment
    currentScopeName = ((ASTIdent) node.jjtGetChild(0)).getValue();

    if (lengthFuncParList > 0) {
      addString("(");
      for (int i = 1; i <= lengthFuncParList; i++) {
        if (i > 1) {
          addString("; ");
        }
        handleNode(node.jjtGetChild(i)); // AlgParamList
      }
      addString(")");
    }

    if (isFunc) {
      // ReturnType
      handleNode(node.jjtGetChild(node.jjtGetNumChildren() - 1));
    }

    addString(";");
  }

  @Override
  protected void handleASTAlgParamList(ASTAlgParamList node) {
    handleNode(node.jjtGetChild(0)); // ParamKind
    handleNode(node.jjtGetChild(1)); // Ident
    addString(": ");
    for (int i = 2; i < node.jjtGetNumChildren(); i++) {
      // [Pointer()] (Type() | ArrayType())
      handleNode(node.jjtGetChild(i));
    }
  }

  @Override
  protected void handleASTAndExpr(ASTAndExpr node) {
    handleNode(node.jjtGetChild(0)); // RelExpr
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      addString(" AND ");
      handleNode(node.jjtGetChild(i)); // RelExpr
    }
  }

  @Override
  protected void handleASTArraySpec(ASTArraySpec node) {
    handleNode(node.jjtGetChild(0)); // (Integer | Ident)
    addString("..");
    if (node.jjtGetNumChildren() > 1) {
      handleNode(node.jjtGetChild(1)); // [(Integer | Ident)]
    }
  }

  @Override
  protected void handleASTArrayType(ASTArrayType node) {
    addString("ARRAY [", node.getLineNumber());
    handleNode(node.jjtGetChild(0)); // ArraySpec
    for (int i = 1; i < node.jjtGetNumChildren() - 1; i++) {
      // ("," ArraySpec)*
      addString(", ");
      handleNode(node.jjtGetChild(i)); // ArraySpec
    }
    addString("] OF ");
    handleNode(node.jjtGetChild(node.jjtGetNumChildren() - 1)); // Type
  }

  @Override
  protected void handleASTAssignStat(ASTAssignStat node) {
    ASTNodeExt temp = (ASTNodeExt) node.jjtGetChild(1); // Expr
    // check if AssignStat contains NewStat
    while (temp.jjtGetNumChildren() > 0 && !temp.getClass().equals(ASTNewStat.class)) {
      temp = (ASTNodeExt) temp.jjtGetChild(0);
    }
    final boolean newStat = temp.getClass().equals(ASTNewStat.class);
    if (newStat) {
      addString("New(", temp.getLineNumber());
      handleNode(node.jjtGetChild(0));
      if (temp.jjtGetNumChildren() > 1) {
        addString(", Init(");
        for (int i = 1; i < temp.jjtGetNumChildren(); i++) {
          if (i > 1) {
            addString(", ");
          }
          handleNode(temp.jjtGetChild(i)); // ActParamList
        }
        addString(")");
      }
      addString(")");
    } else {
      handleNode(node.jjtGetChild(0));
      addString(" := ");
      handleNode(node.jjtGetChild(1));
    }
  }

  @Override
  protected void handleASTAttrSpec(ASTAttrSpec node) {
    if (node.getIsPublic()) {
      addString("PUBLIC ", node.getLineNumber());
    } else {
      addString("PRIVATE ", node.getLineNumber());
    }
    if (node.getIsStatic()) {
      addString("(*STATIC*) ", node.getLineNumber());
    }
    handleNode(node.jjtGetChild(0)); // VarSpec
  }

  @Override
  protected void handleASTBlock(ASTBlock node) {
    addString("BEGIN ", node.getLineNumber());
    enterScope();
    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
      handleNode(node.jjtGetChild(i)); // (Stat)*
    }
    leaveScope();
    addNewLineAndString("END");
  }

  @Override
  protected void handleASTBreakStat(ASTBreakStat node) {
    addString("BREAK", node.getLineNumber());
  }

  @Override
  protected void handleASTCallStat(ASTCallStat node) {
    handleNode(node.jjtGetChild(0)); // Qualifier
    addString("(");
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      if (i > 1) {
        addString(", ");
      }
      handleNode(node.jjtGetChild(i)); // ActParamList
    }
    addString(")");
  }

  @Override
  protected void handleASTChar(ASTChar node) {
    addString(node.getValue(), node.getLineNumber());
  }

  @Override
  protected void handleASTClassType(ASTClassType node) {
    if (node.getIsAbstract()) {
      addString("(*ABSTRACT*) ", node.getLineNumber());
    }
    addString("OBJECT", node.getLineNumber());
    if (node.getIsBasedOn()) {
      addString("(");
      handleNode(node.jjtGetChild(0)); // Ident
      inheritanceRelationship.put(((ASTIdent) node.jjtGetParent().jjtGetChild(0)).getValue(), ((ASTIdent) node.jjtGetChild(0)).getValue());
      addString(")");
    }
    enterScope();
    for (int i = node.getIsBasedOn() ? 1 : 0; i < node.jjtGetNumChildren(); i++) {
      handleNode(node.jjtGetChild(i)); // (AttrSpec() | MethodDecl())*
    }
    leaveScope();
    addNewLineAndString("END");
  }

  @Override
  protected void handleASTCompoundSpec(ASTCompoundSpec node) {
    handleNode(node.jjtGetChild(0)); // IdentList
    addString(": ");
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      // [Pointer()] (Type() | ArrayType())
      handleNode(node.jjtGetChild(i));
    }
    addString(";");
  }

  @Override
  protected void handleASTCompoundType(ASTCompoundType node) {
    addString("RECORD ", node.getLineNumber());
    enterScope();
    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
      handleNode(node.jjtGetChild(i)); // (CompoundSpec)+
    }
    leaveScope();
    addNewLineAndString("END");
  }

  @Override
  protected void handleASTConstDecl(ASTConstDecl node) {
    enterScope();
    addString("CONST ", node.getLineNumber());
    handleNode(node.jjtGetChild(0)); // Ident
    addString(" = ");
    handleNode(node.jjtGetChild(1)); // (Integer | Real)
    addString(";");
    leaveScope();
  }

  @Override
  protected void handleASTDeleteStat(ASTDeleteStat node) {
    addString("Dispose(", node.getLineNumber());
    handleNode(node.jjtGetChild(0)); // Qualifier
    addString(")");
  }

  @Override
  protected void handleASTElseBlock(ASTElseBlock node) {
    addString("ELSE BEGIN ", node.getLineNumber());
    enterScope();
    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
      handleNode(node.jjtGetChild(i)); // (Stat)*
    }
    leaveScope();
    addNewLineAndString("END");
  }

  @Override
  protected void handleASTExpr(ASTExpr node) {
    handleNode(node.jjtGetChild(0)); // OrExpr
  }

  @Override
  protected void handleASTExprList(ASTExprList node) {
    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
      if (i > 0) {
        addString(", ");
      }
      handleNode(node.jjtGetChild(i)); // Expr
    }
  }

  @Override
  protected void handleASTFact(ASTFact node) {
    // CallStat, NewStat, AddrOfStat, Integer, Real, String, Char,
    // Qualifier, FalseFact, TrueFact, NullFact or NestedExpr
    handleNode(node.jjtGetChild(0));
  }

  @Override
  protected void handleASTFalseFact(ASTFalseFact node) {
    addString("FALSE", node.getLineNumber());
  }

  @Override
  protected void handleASTForStat(ASTForStat node) {
    addString("FOR ", node.getLineNumber());
    handleNode(node.jjtGetChild(0)); // Ident
    addString(" := ");
    handleNode(node.jjtGetChild(1)); // Integer
    addString(" TO ");
    handleNode(node.jjtGetChild(2)); // Expr
    addString(" DO BEGIN ");
    enterScope();
    for (int i = 3; i < node.jjtGetNumChildren(); i++) {
      handleNode(node.jjtGetChild(i)); // (Stat)*
    }
    leaveScope();
    addNewLineAndString("END");
  }

  @Override
  protected void handleASTHaltStat(ASTHaltStat node) {
    addString("HALT", node.getLineNumber());
  }

  @Override
  protected void handleASTIdent(ASTIdent node) {
    addString(node.getValue(), node.getLineNumber());
  }

  @Override
  protected void handleASTIdentList(ASTIdentList node) {
    handleNode(node.jjtGetChild(0)); // Ident
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      addString(", ");
      handleNode(node.jjtGetChild(i)); // Ident
    }
  }

  @Override
  protected void handleASTIfStat(ASTIfStat node) {
    addString("IF ", node.getLineNumber());
    handleNode(node.jjtGetChild(0));
    addString(" THEN BEGIN ");
    enterScope();
    final boolean hasElse = node.jjtGetChild(node.jjtGetNumChildren() - 1).getClass().equals(ASTElseBlock.class);

    int count = hasElse ? node.jjtGetNumChildren() - 1 : node.jjtGetNumChildren();

    for (int i = 1; i < count; i++) {
      handleNode(node.jjtGetChild(i)); // Stat
    }
    leaveScope();
    addNewLineAndString("END ");
    if (hasElse) {
      // ElseBlock
      handleNode(node.jjtGetChild(node.jjtGetNumChildren() - 1));
    }
  }

  @Override
  protected void handleASTInheritedFact(ASTInheritedFact node) {
    addString("INHERITED ", node.getLineNumber());
  }

  @Override
  protected void handleASTInputStat(ASTInputStat node) {
    addString("ReadLn(", node.getLineNumber());
    if (node.jjtGetNumChildren() > 0) {
      handleNode(node.jjtGetChild(0)); // Qualifier
    }
    addString(")");
  }

  @Override
  protected void handleASTInteger(ASTInteger node) {
    addString(node.getValue(), node.getLineNumber());
  }

  @Override
  protected void handleASTMethodDecl(ASTMethodDecl node) {
    if (node.getIsPublic()) {
      addString("PUBLIC ", ((ASTAlgHead) node.jjtGetChild(0).jjtGetChild(0)).getLineNumber());
    } else {
      addString("PRIVATE ", ((ASTAlgHead) node.jjtGetChild(0).jjtGetChild(0)).getLineNumber());
    }
    if (node.getValue() != null) {
      if (node.getValue().equals("static")) {
        addString("(*STATIC*) ", node.getLineNumber());
      } else if (node.getValue().equals("abstract")) {
        addString("(*ABSTRACT*) ", node.getLineNumber());
      }
    }
    handleNode(node.jjtGetChild(0));

    // mark all methods virtual per default
    addString(" VIRTUAL; ", node.getLineNumber());
  }

  @Override
  protected void handleASTMethodDef(ASTMethodDef node) {
    // skip first child (Ident) -> handled in AlgHead
    handleNode(node.jjtGetChild(1)); // AlgDef
  }

  @Override
  protected void handleASTNestedExpr(ASTNestedExpr node) {
    addString("(");
    handleNode(node.jjtGetChild(0)); // Expr
    addString(")");
  }

  @Override
  protected void handleASTNewStat(ASTNewStat node) {
    // not needed
  }

  @Override
  protected void handleASTNotFact(ASTNotFact node) {
    if (node.isHasNot()) {
      addString("NOT ", node.getLineNumber());
    }
    handleNode(node.jjtGetChild(0)); // Fact
  }

  @Override
  protected void handleASTNullFact(ASTNullFact node) {
    addString("NIL", node.getLineNumber());
  }

  @Override
  protected void handleASTOrExpr(ASTOrExpr node) {
    handleNode(node.jjtGetChild(0)); // AndExpr
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      addString(" OR ");
      handleNode(node.jjtGetChild(i)); // AndExpr
    }
  }

  @Override
  protected void handleASTOutputStat(ASTOutputStat node) {
    addString("WriteLn(", node.getLineNumber());
    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
      if (i > 0) {
        addString(", ");
      }
      handleNode(node.jjtGetChild(i)); // SingleOutput
    }
    addString(")");
  }

  @Override
  protected void handleASTParamKind(ASTParamKind node) {
    if (!node.getValue().equals("in")) {
      addString("VAR ", node.getLineNumber());
    }
  }

  @Override
  protected void handleASTPointer(ASTPointer node) {
    addString("^", node.getLineNumber());
  }

  @Override
  protected void handleASTProgram(ASTProgram node) {
    addString("PROGRAM ", node.getLineNumber());
    handleNode(node.jjtGetChild(0)); // Ident
    addString(";");
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      // (ConstDecl | TypeDecl |VarDecl | AlgDef | MethodDef)* Block
      handleNode(node.jjtGetChild(i));
    } // for
    addString(".");
  }

  @Override
  protected void handleASTQualifier(ASTQualifier node) {
    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
      if (node.jjtGetChild(i) instanceof ASTIdent
          && ((ASTIdent) node.jjtGetChild(i)).getValue().equals(inheritanceRelationship.get(currentScopeName))) {
        addString("Init");
      } else {
        if (((SimpleNode) node.jjtGetChild(i)).getClass().equals(ASTExprList.class)) {
          addString("[");
        }
        handleNode(node.jjtGetChild(i)); // Ident, Selector or ExprList
        if (((SimpleNode) node.jjtGetChild(i)).getClass().equals(ASTExprList.class)) {
          addString("]");
        }
      }
    }
  }

  @Override
  protected void handleASTReal(ASTReal node) {
    addString(node.getValue(), node.getLineNumber());
  }

  @Override
  protected void handleASTRelExpr(ASTRelExpr node) {
    handleNode(node.jjtGetChild(0)); // SimpleExpr
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      if (node.getValue().equals("!=")) {
        addString(" <> ");
      } else {
        addString(" " + node.getValue() + " ");
      }
      handleNode(node.jjtGetChild(i)); // SimpleExpr
    }
  }

  @Override
  protected void handleASTRepeatStat(ASTRepeatStat node) {
    addString("REPEAT ", node.getLineNumber());
    enterScope();
    for (int i = 0; i < node.jjtGetNumChildren() - 1; i++) {
      handleNode(node.jjtGetChild(i)); // (Stat)*
    }
    leaveScope();
    addNewLineAndString("UNTIL ");
    handleNode(node.jjtGetChild(node.jjtGetNumChildren() - 1)); // Expr
  }

  @Override
  protected void handleASTReturnStat(ASTReturnStat node) {
    if (node.jjtGetNumChildren() > 0) {
      addString(currentScopeName, node.getLineNumber());
      addString(" := ");
      handleNode(node.jjtGetChild(0)); // Expr
    }
  }

  @Override
  protected void handleASTReturnType(ASTReturnType node) {
    addString(": ");
    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
      // [Pointer()] (Type() | ArrayType())
      handleNode(node.jjtGetChild(i));
    }
  }

  @Override
  protected void handleASTSelector(ASTSelector node) {
    // TODO: change to use the pointer class (maybe...)
    if (node.jjtGetParent().jjtGetChild(0).getClass().equals(ASTInheritedFact.class)) {

    } else if (node.getValue().equals("->")) {
      if (node.jjtGetParent().jjtGetChild(0).getClass().equals(ASTThisFact.class)) {
        addString(".", node.getLineNumber());
      } else if (node.jjtGetNumChildren() > 0) {
        addString("^.", node.getLineNumber());
      } else {
        addString("^", node.getLineNumber());
      }
    } else {
      addString(node.getValue(), node.getLineNumber());
    }
    if (node.jjtGetNumChildren() > 0) {
      handleNode(node.jjtGetChild(0));
    }
  }

  @Override
  protected void handleASTSimpleExpr(ASTSimpleExpr node) {
    addString(node.getSign(), node.getLineNumber());
    handleNode(node.jjtGetChild(0)); // Term
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      addString(" " + node.getValues().get(i - 1) + " ");
      handleNode(node.jjtGetChild(i)); // Term
    }
  }

  @Override
  protected void handleASTSingleOutput(ASTSingleOutput node) {
    handleNode(node.jjtGetChild(0)); // Expr
  }

  @Override
  protected void handleASTStart(ASTStart node) {
    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
      handleNode(node.jjtGetChild(i)); // Program
    }
  }

  @Override
  protected void handleASTStat(ASTStat node) {
    // all kinds of Stat and Block
    handleNode(node.jjtGetChild(0));
    addString(";");
  }

  @Override
  protected void handleASTString(ASTString node) {
    addString("'" + node.getValue().substring(1, node.getValue().length() - 1) + "'", node.getLineNumber());
  }

  @Override
  protected void handleASTTerm(ASTTerm node) {
    handleNode(node.jjtGetChild(0)); // NotFact
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      String temp = node.getValues().get(i - 1);
      if (temp.equals("%")) {
        temp = "mod";
      }
      addString(" " + temp + " ");
      handleNode(node.jjtGetChild(i)); // NotFact
    }
  }

  @Override
  protected void handleASTTextStat(ASTTextStat node) {
    addString("(* todo: ", node.getLineNumber());
    handleNode(node.jjtGetChild(0)); // String
    addString(" *)");
  }

  @Override
  protected void handleASTThisFact(ASTThisFact node) {
    addString("SELF", node.getLineNumber());
  }

  @Override
  protected void handleASTTrueFact(ASTTrueFact node) {
    addString("TRUE", node.getLineNumber());
  }

  @Override
  protected void handleASTType(ASTType node) {
    if (node.getValue() == "int") {
      addString("INTEGER", node.getLineNumber());
    } else if (node.getValue() == "bool") {
      addString("BOOLEAN", node.getLineNumber());
    } else if (node.getValue() == "real") {
      addString("REAL", node.getLineNumber());
    } else if (node.getValue() == "char") {
      addString("CHAR", node.getLineNumber());
    } else if (node.getValue() == "string") {
      addString("STRING", node.getLineNumber());
    } else {
      addString(node.getValue(), node.getLineNumber());
    }
  }

  @Override
  protected void handleASTTypeDecl(ASTTypeDecl node) {
    enterScope();
    addString("TYPE ", node.getLineNumber());
    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
      handleNode(node.jjtGetChild(i)); // TypeSpec
    }
    leaveScope();
  }

  @Override
  protected void handleASTTypeSpec(ASTTypeSpec node) {
    enterScope();
    handleNode(node.jjtGetChild(0)); // Ident
    addString(" = ");
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      // (([Pointer()] (Type() | ArrayType() | CompoundType())) | ClassType())
      handleNode(node.jjtGetChild(i));
    }
    addString(";");
    leaveScope();
  }

  @Override
  protected void handleASTVarDecl(ASTVarDecl node) {
    enterScope();
    addString("VAR ", node.getLineNumber());
    enterScope();
    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
      handleNode(node.jjtGetChild(i));
    }
    leaveScope();
    leaveScope();
  }

  @Override
  protected void handleASTVarSpec(ASTVarSpec node) {
    handleNode(node.jjtGetChild(0)); // IdentList
    addString(": ");
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      // [Pointer()] (Type() | ArrayType() | CompoundType())
      handleNode(node.jjtGetChild(i));
    }
    addString(";");
  }

  @Override
  protected void handleASTWhileStat(ASTWhileStat node) {
    addString("WHILE ", node.getLineNumber());
    handleNode(node.jjtGetChild(0)); // Expr
    addString(" DO BEGIN ");
    enterScope();
    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
      handleNode(node.jjtGetChild(i)); // (Stat)*
    }
    leaveScope();
    addNewLineAndString("END");
  }

}
